module ALU(in1, in2, func, sub, out);
	input signed [31:0] in1, in2;
	input [5:0] func;
	input sub;
	output [31:0] out;
	
	wire [31:0] andval = in1 & in2,
					orval  = in1 | in2,
					xorval = in1 ^ in2,
					addval = in1 + sin2 + sub,
					sin2 = sub ? ~in2 : in2,
					lohi 	 = {in2[15:0],in1[15:0]};
	wire			eq = (in1 == in2),
					lt = (in1 < in2),
					le = lt | eq;
					
					
	wire [2:0] func_major = func[5:3];
	wire [1:0] func_minor = func[1:0];
	wire outnot = func[2];
	
	reg [31:0] logic;
	always @(func_minor or andval or orval or xorval) begin
		case(func_minor)
			AND: logic = andval;
			OR:  logic = orval;
			XOR: logic = xorval;
			default: logic = {32{1'bX}};
		endcase
	end
	wire [31:0] logicout = outnot ? ~logic : logic;
	
	reg comp;
	always @(func_minor or eq or lt or le) begin
		case(func_minor)
			EQ: comp = eq;
			LT: comp = lt;
			LE: comp = le;
			default: logic = {32{1'bX}};
		endcase
	end
	wire compout_pre = outnot ? !comp : comp;
	wire [31:0] compout = { {31{1'b0}}, compout_pre};
	
	always @(func_major or logicout or addval or compout or lohi) begin
		case(func_major)
			LOGIC: aluout = logicout;
			ARITH: aluout = addval;
			COMP:  aluout = compout;
			LOHI:  aluout = lohi;
			default:  aluout={32{1'bX}};
		endcase
	end
	
	reg [31:0] aluout;
	
	assign out = aluout;
	
	parameter AND = 2'o1;
	parameter OR  = 2'o2;
	parameter XOR = 2'o3;
	
	parameter EQ = 2'o1;
	parameter LT  = 2'o2;
	parameter LE = 2'o3;
	
	parameter LOGIC = 3'o1;
	parameter ARITH = 3'o2;
	parameter COMP = 3'o4;
	parameter LOHI  = 3'o7;
	
	parameter OP2_AND =6'o11;
	parameter OP2_OR  =6'o12;
	parameter OP2_XOR =6'o13;
	parameter OP2_NAND=6'o15;
	parameter OP2_NOR =6'o16;
	parameter OP2_NXOR=6'o17;
	parameter OP2_ADD =6'o20;
	parameter OP2_SUB =6'o21;
	parameter OP2_EQ  =6'o41;
	parameter OP2_LT  =6'o42;
	parameter OP2_LE  =6'o43;
	parameter OP2_NE  =6'o45;
	parameter OP2_GE  =6'o46;
	parameter OP2_GT  =6'o47;
	parameter OP2_LOHI=6'o77;
endmodule